/*
 * img.c
 *
 *  Created on: 05/11/2008
 *      Author: arkanrow
 */
#include <img.h> /*uncomment when put in Minix*/
/*#include "img.h"  comment when put in Minix*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#define WINDOW 64*1024
#define VESA_SET_COLORKEY 1

int screenFD = -1;
int screenRefCount = 0;

typedef struct{
        unsigned short type;
        unsigned long size;
        unsigned short reserved1;
        unsigned short reserved2;
        unsigned long offsetbits;
}BITMAPFILEHEADER;

typedef struct{
        unsigned long size;
        unsigned long width;
        unsigned long height;
        unsigned short planes;
        unsigned short bitcount;
        unsigned long compression;
        unsigned long sizeimage;
        long xpelspermeter;
        long ypelspermeter;
        unsigned long colorsused;
        unsigned long colorsimportant;
}BITMAPINFOHEADER;

/*BMP functions*/
static int readHeaders(FILE * file, imgT * img);
static int readImage(FILE * file, imgT * img);


resT resolutions[]={{320,200},{640,480},{800,600},{1024,768},{1280,1024}};

imgT * imgLoad(char * fileName, formatT format)
{
	FILE * img;
	imgT * ret;

	if (format != IMG_BMP)
		return NULL;

	if ((img = fopen(fileName, "r")) == NULL)
	{
		printf("Failed to open %s\n", fileName);
		return NULL;
	}

	if ((ret = (imgT*)malloc(sizeof(imgT))) == NULL)
	{
		fclose(img);
		printf("Not enough memory\n");
		return NULL;
	}

	if (readHeaders(img, ret) == 0)
	{
		fclose(img);
		printf("libimg: Bad headers\n");
		return NULL;
	}

	if ((ret->image = (char*)malloc(3 * ret->h * ret->w)) == NULL)
	{
		fclose(img);
		free(ret);
		printf("Not enough memory\n");
		return NULL;
	}

	if (readImage(img, ret) == 0)
	{
		fclose(img);
		free(ret->image);
		free(ret);
		return NULL;
	}

	fclose(img);
	return ret;
}

imgT * imgCreate(unsigned w, unsigned h)
{
	imgT * ret;
	int i;

	/*Alloc space for the data type and image buffer*/
	if ((ret = (imgT*)malloc(sizeof(imgT))) == NULL)
		return NULL;

	if ((ret->image = (char*)malloc(w*h*3*sizeof(char))) == NULL)
	{
		free(ret);
		return NULL;
	}

	/*Clear the image*/
	for (i=0; i < 3 * sizeof(char) * h * w; i++)
		ret->image[i] = 0;

	/*Set width heigth and format*/
	ret->w = w;
	ret->h = h;
	ret->format = IMG_BMP;

	return ret;
}


imgT * imgScreen(int res)
{
	imgT * ret;
	int i;
	int w,h;

	if (res<0 || res > sizeof(resolutions)/sizeof(resT))
		return NULL;

	w=resolutions[res].w;
	h=resolutions[res].h;

	/*Alloc space for the data type and image buffer*/
	if ((ret = (imgT*)malloc(sizeof(imgT))) == NULL)
		return NULL;

	if ((ret->image = (char*)malloc(h*w*3*sizeof(char))) == NULL)
	{
		free(ret);
		return NULL;
	}

	if (screenFD == -1)
		if ((screenFD = open("/dev/vesa", O_RDWR)) == -1)
		{
			free(ret->image);
			free(ret);
			return NULL;
		}


	if (ioctl(screenFD,res,(void*)0) )
	{
		close(screenFD);
		screenFD=-1;
		free(ret->image);
		free(ret);
		return NULL;
	}


	/*Set width heigth and format*/
	ret->w = w;
	ret->h = h;
	ret->format = IMG_SCREEN;

	/*Clear the screen*/
	for (i=0; i < 3 * sizeof(char) * ret->h * ret->w; i++)
		ret->image[i] = 0;
	imgScreenRefresh(ret);

	screenRefCount++;
	return ret;
}

void imgFree(imgT * img)
{
	if (img->format == IMG_SCREEN && --screenRefCount == 0)
	{
		close(screenFD);
		screenFD = -1;
	}

	free(img->image);
	free(img);
}

void imgScreenRefresh(imgT * scr)
{
	int i;

	int screen_bytes= scr->h*scr->w*3;

	lseek(screenFD, 0, SEEK_SET);

	for (i=0; i<screen_bytes; i+=WINDOW)
		write(screenFD, scr->image + i,
				i+WINDOW > screen_bytes? screen_bytes - i : WINDOW);
}

void setPixel(imgT * img, pixelT p)
{
	if (img == NULL || p.point.x >= img->w || p.point.y >= img->h ||
			p.point.x < 0 ||  p.point.y < 0)
		return;

	img->image[3*(p.point.x + img->w * p.point.y) + 0] = p.color.b;
	img->image[3*(p.point.x + img->w * p.point.y) + 1] = p.color.g;
	img->image[3*(p.point.x + img->w * p.point.y) + 2] = p.color.r;
}

pixelT getPixel(imgT * img, pointT p)
{
	pixelT ret = {{0, 0}, {0, 0, 0}};

	if (img == NULL || p.x >= img->w || p.y >= img->h ||
			p.x < 0 ||  p.y < 0)
		return ret;

	ret.point = p;
	ret.color.b = img->image[3*(p.x + img->w * p.y) + 0];
	ret.color.g = img->image[3*(p.x + img->w * p.y) + 1];
	ret.color.r = img->image[3*(p.x + img->w * p.y) + 2];

	return ret;
}

void imgClear(imgT * img, colorT color)
{
	register int i, j;
	pixelT pix;
	int r, g, b, w, h;
	register char * bitmap;
	/*Variables utilizadas dentro del for y requeridas.*/
	register int iLimit, jLimit, iPhase, jPhase;

	if (img == NULL)
		return;

	r = color.r;
	g = color.g;
	b = color.b;
	bitmap = img->image;
	w = img->w;
	h = img->h;

	/** Loop Control: */
	iLimit = sizeof(char) * w;
	jLimit = sizeof(char) * h;

	/*Clear the screen*/
	for (i=0; i < iLimit; i++)
	{
		iPhase = i*3;
		for (j=0; j < jLimit; j++)
		{
			jPhase = j*w*3;
			bitmap[iPhase+jPhase + 0] = b;
			bitmap[iPhase+jPhase + 1] = g;
			bitmap[iPhase+jPhase + 2] = r;
		}
	}
}

static int readHeaders(FILE * file, imgT * img)
{
	BITMAPFILEHEADER fileHeader;
	BITMAPINFOHEADER infoHeader;

	/*Read the file header information*/
	if( (fread(&fileHeader.type, sizeof(unsigned short), 1, file )) == 0)
		return 0;

	if( (fread(&fileHeader.size, sizeof(unsigned long), 1, file )) == 0)
		return 0;

	if( (fread(&fileHeader.reserved1, sizeof(unsigned short), 1, file )) == 0)
		return 0;

	if( (fread(&fileHeader.reserved2, sizeof(unsigned short), 1, file )) == 0)
		return 0;

	if( (fread(&fileHeader.offsetbits, sizeof(unsigned long), 1, file )) == 0)
		return 0;

	/*Read the image header information*/
	if( (fread(&infoHeader.size, sizeof(unsigned long), 1, file )) == 0)
		return 0;

	if( (fread(&infoHeader.width, sizeof(unsigned long), 1, file )) == 0)
		return 0;

	if( (fread(&infoHeader.height, sizeof(unsigned long), 1, file )) == 0)
		return 0;

	if( (fread(&infoHeader.planes, sizeof(unsigned short), 1, file )) == 0)
		return 0;

	if( (fread(&infoHeader.bitcount, sizeof(unsigned short), 1, file )) == 0)
		return 0;

	if( (fread(&infoHeader.compression, sizeof(unsigned long), 1, file )) == 0)
		return 0;

	if( (fread(&infoHeader.sizeimage, sizeof(unsigned long), 1, file )) == 0)
		return 0;

	if( (fread(&infoHeader.xpelspermeter, sizeof(long), 1, file )) == 0)
		return 0;

	if( (fread(&infoHeader.ypelspermeter, sizeof(long), 1, file )) == 0)
		return 0;

	if( (fread(&infoHeader.colorsused, sizeof(unsigned long), 1, file )) == 0)
		return 0;

	if( (fread(&infoHeader.colorsimportant, sizeof(unsigned long), 1, file )) == 0)
		return 0;

	/*Check the format is correct*/
	if (fileHeader.type != 19778)
	{
		printf("libimg: Image not in BMP format\n");
		return 0;
	}

	/*Now suports 24 bit format!!!!!!!*/
	if (infoHeader.bitcount != 32 && infoHeader.bitcount != 24)
	{
		printf("libimg: Warning! Image not a 32bit nor 24bit BMP, but a %dbit BMP!\n",infoHeader.bitcount);
	}

	img->w = infoHeader.width;
	img->h = infoHeader.height;
	img->bits=infoHeader.bitcount;
	return 1;
}

int getResolutionCode(imgT *img)
{
	int i;

	for (i=0;i<sizeof(resolutions);i++)
	{
		if (resolutions[i].w==img->w && resolutions[i].h==img->h)
			return i; 
	}

	return -1;
}

static int readImage(FILE * file, imgT * img)
{
	char pixel[4];
	int i, j, pixIndex;

	for (i=img->h-1; i>=0; i--)
		for (j=0; j<img->w; j++)
		{
			if((fread(pixel, (img->bits)/8 * sizeof(char), 1, file )) == 0)
			{
				printf("libimg: Image file shorter than expected\n");
				printf("libimg: Expected %dx%d %d bytes\n", img->w, img->h, img->w*img->h*41);
				printf("libimg: Reached end in x=%d, y=%d\n", j, img->h-i);
				return 0;
			}

			pixIndex = (i * img->w + j) * 3;
			img->image[pixIndex + 0] = pixel[0];
			img->image[pixIndex + 1] = pixel[1];
			img->image[pixIndex + 2] = pixel[2];
		}

	return 1;
}
